
#include <iostream>
#include <string>
#include <vector>
#include <cstdlib>
#include <cassert>
#include <ctime>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>


#define  NUM_OF_Fork 5

#define SRAND() srand((unsigned long)time(NULL) ^ getpid() ^ 0x114514 )


typedef void(*task)();

// 函数指针这一步挺关键的，把抽象的函数转变为类型
//随机任务，随机发给给个管道
//实现一个父进程通过匿名管道控制子进程的方法
// 用宏函数创建种子

class Pipes
{
public:
   //构造函数
   Pipes( int writePid , pid_t pid)
   :_writePid(writePid)
   ,_pid(pid)

   {
       char buffer[1024];
       sprintf(buffer,"%d号管道",number++);
       _name = buffer;
   }

public:
  static int number;  
  string _name;  // 虽然是匿名管道，但是我们还是能给他编一个号的，以便于标识
  int _writePid;
  pid_t _pid;
};

static Pipes::number = 0;

void add()
{
    int x1,x2;
    std::cin >> x1 >> x2;
    std::cout<< "x1 + x2 = " << x1 + x2 << std::endl;
    sleep(1);
}

void sub()
{
    int x1,x2;
    std::cin >> x1 >> x2;
    std::cout<< "x1 - x2 = " << x1 - x2 << std::endl;
    sleep(1);
}

void mul()
{
    int x1,x2;
    std::cin >> x1 >> x2;
    std::cout<< "x1 * x2 = " << x1 * x2 << std::endl;
    sleep(1);
}

void div()
{
    int x1,x2;
    std::cin >> x1 >> x2;
    std::cout<< "x1 / x2 = " << x1 / x2 << std::endl;
    sleep(1);
}



// 将任务加入执行序列
void LoadTask(vector<task>& taskTable)
{
     taskTable.push_back(add);
     taskTable.push_back(sub);
     taskTable.push_back(mul);
     taskTable.push_back(div);
}

// 创建完成之后，父进程关闭读端（发命令），子进程关闭写端（接受命令并执行）
void CreatPipesAndFork(vector<Pipes>& pipesTable, vector<task>& taskTable)
{
    
    for(int i = 0 ; i < NUM_OF_Fork ; i++)
    {
        int fd = fork();
        int fd_arr[2];
        pipe(fd_arr);
         // 分流
        if(fd == 0) 
        {
            //关闭子进程写端
            close(fd_arr[1]);

            //子进程不必退出，而是阻塞在这里等待执行,父进程继续去执行其他的代码

            while(true)
            {

               // 难点，子进程共享的代码是怎么样的规则，如果子进程在函数里面呢
               // 还是说分流了就读不到了
               int task = GetTack(fd_arr[0]);
               taskTable[task]();
            }        

            exit(0);
        }
            // 关闭父进程读端 , 这个管道就创建好了
        close(fd_arr[0]);
        Pipes pipePool(fd_arr[1],fd);
        pipesTable.push_back(pipePool);
    }

}

bool SendCommnd(vector<task>& taskTable,vector<Pipes>& pipesTable,int indexPipe,int indexTask)
{
     // 直接的发送的就是indexTask信号
     int fd = pipesTable[indexPipe]._writePid;
     int buffer = indexTask; 
     int n = write(fd,&buffer,sizeof(int));
     if(n != 4)
     return false;

     return true;
}

int GetTack(pid_t pid)
{
    int buffer = 0;
    int n = read(pid,&buffer,sizeof(int));
    if(n == 4)
    {
       return buffer;
    }
    else
    {
        std::cout << "什么也没读到" << std::endl;   
        return -1;
    }

    return buffer;
}

DestroyedPipe(vector<Pipes>& pipesTable)
{
    for(int i = 0 ;  i < pipesTable.size() ; i++)
    {
        // 关闭父进程写端,递归式的一个一个关闭
        close(pipesTable[i]._writePid);  
    }
}

void wait(std::vector<task>& taskTable)
{
    for(int i = 0 ; i < taskTable.size() ; i++)
    {
       int ret = waitpid(taskTable[i]._pid,nullptr,0);
       if(ret== taskTable[i]._pid)
       std::cout << "回收成功" << std::endl; 
    }
}

int main()
{   
    // 进程肯定要一个列表来管理的，即进程列表 
    std::vector<task> taskTable;
    std::vector<Pipes> pipesTable; 
    CreatPipesAndFork(pipesTable);
    LoadTask(taskTable);

    // 准备工作已经完成，开始给管道发命令
   while(true)
   {  
       // 首先我们要选出，给第几个管道发信息
       int indexPipe = rand() % NUM_OF_Fork;

       // 其次我们要选出任务
       int indexTask = rand() % taskTable.size();

       // 正式发送
      bool ret = SendCommnd(indexPipe,indexTask);
      if(!ret)
      {
         std::cout << "发送失败" << std::endl;
         break;
      }
         
   }

   

    DestroyedPipe();
    // 回收子进程
    wite();
    return 0;
}
